import {RecordType} from "./record";

function MemoryAspect(timestamp, diffSize, rec, block) {
    this.timestamp = timestamp;
    this.diffSize = diffSize;
    if (Array.isArray(rec)) {
        this.recs = rec;
        this.blocks = block;
    } else {
        this.recs = [rec];
        this.blocks = [block];
    }
}

export class MemoryPool {
    constructor(streamId, addressesToBlock) {
        this.streamId = streamId;
        this.tsAndTotalSizes = [];
        this.tsAndAllocSizes = [];
        this.tsAndBorrowSizes = []
        this.addressesToBlock = addressesToBlock;
        this.poolAddressesToSize = new Map();
    }
    addRecord(rec) {
        if (rec.type === RecordType.poolExpand) {
            this.tsAndTotalSizes.push(new MemoryAspect(rec.timestamp, parseInt(rec.size), rec, null));
            this.poolAddressesToSize.set(rec.address, rec.size);
            return;
        }
        if (rec.type === RecordType.poolShrink) {
            this.tsAndTotalSizes.push(new MemoryAspect(rec.timestamp,
                -parseInt(this.poolAddressesToSize.get(rec.address)), rec, null));
            return;
        }

        if (rec.type === RecordType.allocMem) {
            this.addOrAppendMemoryAspect(this.tsAndAllocSizes, rec);
            return;
        }
        if (rec.type === RecordType.birthRecycle) {
            this.addOrAppendMemoryAspect(this.tsAndAllocSizes, rec, -1);
            return;
        }

        if (rec.type === RecordType.recycle) {  // borrow recycle
            this.addOrAppendMemoryAspect(this.tsAndBorrowSizes, rec);
            return;
        }
        if (rec.type === RecordType.migrationSendEvent) {
            this.addOrAppendMemoryAspect(this.tsAndBorrowSizes, rec, -1);
        }
    }
    addOrAppendMemoryAspect(mas, rec, sizeNeg = 1) {
        let size = NaN;
        let block = this.addressesToBlock.get(rec.address);
        // 外部（profiling-data）应该保证block必须能找到，所以这里不对block做undefined判断
        size = block.size;

        if (mas.length > 0 && mas[mas.length - 1].timestamp.toFixed(3) === rec.timestamp.toFixed(3)) {
            let ma = mas[mas.length - 1];
            ma.diffSize += sizeNeg * size;
            ma.recs.push(rec);
            ma.blocks.push(block);
        } else {
            mas.push(new MemoryAspect(rec.timestamp, sizeNeg * size, rec, block));
        }
    }
    generateDataForApacheEcharts(minTime, maxTime) {
        return {
            totalMemSize: this.generateApacheEchartsFromMemoryAspects(this.tsAndTotalSizes, minTime, maxTime),
            allocatedMemSize: this.generateApacheEchartsFromMemoryAspects(this.tsAndAllocSizes, minTime, maxTime),
            borrowedMemSize: this.generateApacheEchartsFromMemoryAspects(this.tsAndBorrowSizes, minTime, maxTime)
        };
    }
    generateApacheEchartsFromMemoryAspects(mas, minTime, maxTime) {
        let ret = [[minTime, 0], ].concat(mas.map(a => [a.timestamp, a.diffSize]));
        let lastOne = ret[ret.length - 1];
        if (lastOne[0] < maxTime) {
            ret.push([maxTime, 0]);
        }
        for (let i = 1; i < ret.length; ++i) {
            ret[i][1] += ret[i - 1][1];
        }
        return ret;
    }
}